///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Contains OBB-related code. (oriented bounding box)
*	\file		LaborOBB.h
*	\author		Pierre Terdiman
*	\date		January, 13, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABOROBB_H__
#define __LABOROBB_H__
#include "../LaborCoreDef.h"
#include "LaborMatrix4Function.h"
NAMESPACE_LABOR3D_BEGIN
// Forward declarations
class CLLSS;

class  LABORCORE_API CLOBB
{
public:
	//! Constructor
	inline						CLOBB()		{}
	//! Constructor
	inline						CLOBB(const CLPoint3& center, const CLPoint3& extents, const CLMatrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot)	{}
	//! Destructor
	inline						~CLOBB()		{}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Setups an empty OBB.
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void				setEmpty()
	{
		mCenter.zero();
		mExtents.set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);
		mRot.identity();
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Tests if a point is contained within the OBB.
	*	\param		p	[in] the world point to test
	*	\return		true if inside the OBB
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool				containsPoint(const CLPoint3& p)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Builds an OBB from an AABB and a world transform.
	*	\param		aabb	[in] the aabb
	*	\param		mat		[in] the world transform
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void				create(const CLAABB& aabb, const CLMatrix4x4& mat);

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Recomputes the OBB after an arbitrary transform by a 4x4 matrix.
	*	\param		mtx		[in] the transform matrix
	*	\param		obb		[out] the transformed OBB
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	void				rotate(const CLMatrix4x4& mtx, CLOBB& obb)	const
	{
		// The extents remain constant
		obb.mExtents = mExtents;
		// The center gets x-formed
		//obb.mCenter = mCenter * mtx;
		LM_Mul( mtx, mCenter, obb.mCenter );
		// Combine rotations
		obb.mRot = mRot * CLMatrix3x3(mtx);
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Checks the OBB is valid.
	*	\return		true if the box is valid
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	inline	bool				isValid()	const
	{
		// Consistency condition for (Center, Extents) boxes: Extents >= 0.0f
		if(mExtents.x < 0.0f)	return false;
		if(mExtents.y < 0.0f)	return false;
		if(mExtents.z < 0.0f)	return false;
		return true;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Computes the obb planes.
	*	\param		planes	[out] 6 box planes
	*	\return		true if success
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool				computePlanes(CLPlane* planes)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Computes the obb points.
	*	\param		pts	[out] 8 box points
	*	\return		true if success
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool				computePoints(CLPoint3* pts)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Computes vertex normals.
	*	\param		pts	[out] 8 box points
	*	\return		true if success
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool				computeVertexNormals(CLPoint3* pts)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Returns edges.
	*	\return		24 indices (12 edges) indexing the list returned by ComputePoints()
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	const unsigned int*			getEdges()	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Returns local edge normals.
	*	\return		edge normals in local space
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	const CLPoint3*		getLocalEdgeNormals()	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Returns world edge normal
	*	\param		edge_index		[in] 0 <= edge index < 12
	*	\param		world_normal	[out] edge normal in world space
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void				computeWorldEdgeNormal(unsigned int edge_index, CLPoint3& world_normal)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Computes an LSS surrounding the OBB.
	*	\param		lss		[out] the LSS
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	void				computeLSS(CLLSS& lss)	const;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	*	Checks the OBB is inside another OBB.
	*	\param		box		[in] the other OBB
	*	\return		true if we're inside the other box
	*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	bool				isInside(const CLOBB& box)	const;

	inline	const CLPoint3&		getCenter()		const	{ return mCenter;	}
	inline	const CLPoint3&		getExtents()	const	{ return mExtents;	}
	inline	const CLMatrix3x3&	getRot()		const	{ return mRot;		}

	inline	void				getRotatedExtents(CLMatrix3x3& extents)	const
	{
		extents = mRot;
		extents.scale(mExtents);
	}

	CLPoint3				mCenter;		//!< B for Box
	CLPoint3				mExtents;		//!< B for Bounding
	CLMatrix3x3			mRot;			//!< O for Oriented

	// Orientation is stored in row-major format,
	// i.e. rows = eigen vectors of the covariance matrix

};

NAMESPACE_LABOR3D_END

#endif	// __LABOROBB_H__
